﻿using System;
using System.Drawing;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.ADF.CATIDs;
using ESRI.ArcGIS.Framework;
using ESRI.ArcGIS.ArcMapUI;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Editor;
using System.Windows.Forms;
using System.Collections;
using ESRI.ArcGIS.Display;
using ESRI.ArcGIS.SystemUI;
using ESRI.ArcGIS.Controls;
//using EngineDemo_CSharp.General;
using System.Windows.Forms;
//using ESRI.ArcGIS.GeoDatabaseUI;
using ESRI.ArcGIS.SystemUI;
using ESRI.ArcGIS.Geometry;
using System.Collections;
using DIWAGIS.Modules;
using DIWAGIS.Tools;
//using EngineDemo_CSharp.Views;
//using EngineDemo_CSharp.General;

namespace DIWAGIS.EditorTools
{
    /// <summary>
    /// Command that works in ArcMap/Map/PageLayout
    /// </summary>
    [Guid("53adc73f-8fbd-4a48-8739-2b802c604ee6")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("DIWAGIS.EditorTools.CreateJumpsCmds")]
    public sealed class CreateJumpsCmds : BaseCommand
    {
        #region COM Registration Function(s)
        [ComRegisterFunction()]
        [ComVisible(false)]
        static void RegisterFunction(Type registerType)
        {
            // Required for ArcGIS Component Category Registrar support
            ArcGISCategoryRegistration(registerType);

            //
            // TODO: Add any COM registration code here
            //
        }

        [ComUnregisterFunction()]
        [ComVisible(false)]
        static void UnregisterFunction(Type registerType)
        {
            // Required for ArcGIS Component Category Registrar support
            ArcGISCategoryUnregistration(registerType);

            //
            // TODO: Add any COM unregistration code here
            //
        }

        #region ArcGIS Component Category Registrar generated code
        /// <summary>
        /// Required method for ArcGIS Component Category registration -
        /// Do not modify the contents of this method with the code editor.
        /// </summary>
        private static void ArcGISCategoryRegistration(Type registerType)
        {
            string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
            MxCommands.Register(regKey);
            ControlsCommands.Register(regKey);
        }
        /// <summary>
        /// Required method for ArcGIS Component Category unregistration -
        /// Do not modify the contents of this method with the code editor.
        /// </summary>
        private static void ArcGISCategoryUnregistration(Type registerType)
        {
            string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
            MxCommands.Unregister(regKey);
            ControlsCommands.Unregister(regKey);
        }

        #endregion
        #endregion

        private IHookHelper m_hookHelper = null;
        //private IEditor _editor;
        private int _commandSubtype;
        private int _helpId = -1;
        private string _helpFile = ConfigUtil.GetHelpFile();
        private IMap pMap;
        private IMapControl3 m_mapControl;
        //private frm_Main frmMain;
        private IApplication p_app;
        //private IEngineEditor _editor;
        //private IEngineEditor m_editor;
        private IEngineEditor _editor = new EngineEditorClass();
        //private IEngineEditor _editor1;
        private IEngineEditor _editor1 = new EngineEditorClass();
        // Declare configuration variables 
        private double m_jumpDistance = 3;

        //Retrieve configuration from XML config file (overrides defaults set above)
        public void GetConfigSettings()
        {
            ConfigUtil cu = new ConfigUtil();
            m_jumpDistance = cu.GetConfigValue("CreateJumps_Distance", m_jumpDistance);
        }

        public CreateJumpsCmds()
        {
            //base.m_category = "Water Utility Tools";
            base.m_caption = "Create Jumps Over";
            base.m_message = "Adds over jumps to selected overlapping lines.";
            base.m_toolTip = "Nằm trên ống chọn";
            base.m_name = "WaterUtilityTools_CreateJumpsOver";
            try
            {
                if (base.Bitmap == 0)
                {
                   
                        string bitmapResourceName = "CreateJumpsOver.bmp";
                        base.m_bitmap = new Bitmap(GetType(), bitmapResourceName);
                  
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.Message, "Invalid Bitmap");
            }

            //Get any user settings stored in XML file
            GetConfigSettings();
        }


        #region Overriden Class Methods

        public void SetSubType(int SubType)
        {
            _commandSubtype = SubType;

            if (_commandSubtype == 1)
            {
                base.m_caption = "Create Jumps Over";
                base.m_message = "Adds over jumps to selected overlapping lines.";
                base.m_toolTip = "Nằm trên ống chọn";
                base.m_name = "WaterUtilityTools_CreateJumpsOver";
                _helpId = 105;
            }
           

            try
            {
                if (base.Bitmap == 0)
                {
                    if (_commandSubtype == 1)
                    {
                        string bitmapResourceName = "CreateJumpsOver.bmp";
                        base.m_bitmap = new Bitmap(GetType(), bitmapResourceName);
                    }
                    else
                    {
                        string bitmapResourceName = "CreateJumpsUnder.bmp";
                        base.m_bitmap = new Bitmap(GetType(), bitmapResourceName);
                    }
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.Message, "Invalid Bitmap");
            }

            //Get any user settings stored in XML file
            GetConfigSettings();
        }

        public int GetCount()
        {
            return 2;
        }
       

        #region Overridden Class Methods

        /// <summary>
        /// Occurs when this command is created
        /// </summary>
        /// <param name="hook">Instance of the application</param>
        public override void OnCreate(object hook)
        {

            if (hook != null)
            {

                //Check for ArcMap
                if (hook is IMxApplication)
                {

                    m_hookHelper = new HookHelperClass();
                    m_hookHelper.Hook = hook;
                    IApplication app = hook as IApplication;
                    p_app = app;
                    pMap = m_hookHelper.FocusMap;
                    //Get the editor.
                    UID editorUID = new UIDClass();
                    editorUID.Value = "esriControls.ControlsEditingEditTool";
                    IEngineEditor _editor = app.FindExtensionByCLSID(editorUID) as IEngineEditor;
                    IEngineEditor _editor1 = app.FindExtensionByCLSID(editorUID) as IEngineEditor;


                    if (_editor != null)
                    {
                        base.m_enabled = true;
                        return;
                    }
                }
            }

            // On Create disable this command if ArcMap editor was not found.  
            //_editor = null;
            //_editor1 = null;
            base.m_enabled = true;
            return;

            // TODO:  Add other initialization code
        }

        /// <summary>
        /// Occurs when this command is clicked
        /// </summary>
        public override void OnClick()
        {
            // TODO: Add CreateJumpsCmds.OnClick implementation
            CreateJumps();
        }

        #endregion
        public override bool Enabled
        {
            get
            {
                //Enable this command when editing with some features selected.
                if ((_editor != null) &&
                    (_editor.EditState == esriEngineEditState.esriEngineStateEditing) &&(_editor.SelectionCount > 0))
                    return true;
                else
                    //return false;
                    return true;
            }
        }

        public override int HelpContextID
        {
            get
            {
                if (_helpId > -1)
                    return _helpId;
                else
                    return base.HelpContextID;
            }
        }

        public override string HelpFile
        {
            get
            {
                if (!String.IsNullOrEmpty(_helpFile.Trim()))
                    return _helpFile;
                else
                    return base.HelpFile;
            }
        }

        #endregion

        public void CreateJumps()
        {
            try
            {
                //Get editor
                IEngineEditor editor = _editor;
                if (_editor.EditState != esriEngineEditState.esriEngineStateEditing)
                {
                    MessageBox.Show("Must be editing.", this.Tooltip);
                    return;
                }

                ICursor lineCursor;
                IFeature lineFeature;
               
                IApplication app = p_app;
              
                //IMxDocument mxdoc = (IMxDocument)app.Document;
                
                IMap map = editor.Map;
                
                //Get enumeration of feature layers
                UID geoFeatureLayerID = new UIDClass();
                geoFeatureLayerID.Value = "{E156D7E5-22AF-11D3-9F99-00C04F6BC78E}";
                IEnumLayer enumLayer = map.get_Layers(((ESRI.ArcGIS.esriSystem.UID)geoFeatureLayerID), true);
              
                //Get enumeration of editable layers
                IEngineEditLayers eLayers = (IEngineEditLayers)editor;
               
                // Create list of visible line layers with selected feature(s)
                ArrayList lineLayers = new ArrayList();
              
                enumLayer.Reset();
                ILayer layer = enumLayer.Next();
                while (layer != null)
                {
                    IFeatureLayer fLayer = (IFeatureLayer)layer;
                    if (fLayer.Valid && (fLayer.FeatureClass.ShapeType == ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolyline)
                       && (fLayer.Visible))
                    {
                        if (eLayers.IsEditable(fLayer))
                        {
                            IFeatureSelection fSel = fLayer as IFeatureSelection;
                            if (fSel.SelectionSet.Count > 0)
                            {
                                lineLayers.Add(fLayer);
                            }
                        }
                    }
                    layer = enumLayer.Next();
                }
                MessageBox.Show(lineLayers.Count.ToString());
                
                //Prep screen refresh for selected features
                IActiveView activeView = map as IActiveView;
                IInvalidArea invalid = new InvalidAreaClass();
                //invalid.Display = editor.Display;
                invalid.Display = activeView.ScreenDisplay;
                IEnvelope ext;
               
                // Create an edit operation enabling undo/redo
                editor.StartOperation();

                try
                {
                    //IPointCollection polylinePointCollection;
                    IPointCollection linePointCollection = (IPointCollection)new PolylineClass();
                    ISpatialFilter spatialFilter = new SpatialFilterClass();
                    IFeatureCursor featureCursor;
                    IFeature crossFeature;

                    ITopologicalOperator topologicalOP;
                    ITopologicalOperator topologicalOP2;
                    IPointCollection intersectPointCollection;
                    //IPolyline focusPolyline;
                    IPoint intersectPoint = new PointClass();
                    IPoint outPoint = new PointClass();
                    IPoint beginPoint;
                    IPoint endPoint;

                    //ICurve lineCurve;
                    double totalDistance;
                    double distAlongCurve = 0;
                    double distFromCurve = 0;
                    bool boolRightSide = false;

                    ISegmentCollection segmentColl;
                    ISegmentCollection segmentCollNew;
                    IConstructCircularArc constructCircArc;
                    ISegment curveSegment;
                    IProximityOperator proximityOP;
                    double pointDistance = 0;

                    bool projectOnto;
                    bool createPart;
                    bool splitHappenedBefore;
                    bool splitHappenedAfter;
                    int newPartIndexBegin;
                    int newSegmentIndexBegin;
                    int newPartIndexEnd;
                    int newSegmentIndexEnd;
                    IPolycurve3 selCurve;
                    IPolycurve3 selCurveB;

                    object _missing = Type.Missing;

                    IFeatureSelection lineSel;

                    // Step through all line layers with selection sets                
                    foreach (IFeatureLayer lineLayer in lineLayers)
                    {

                        // Get cursor of selected lines 
                        lineSel = (IFeatureSelection)lineLayer;
                        ISelectionSet2 sel = lineSel.SelectionSet as ISelectionSet2;
                        sel.Search(null, false, out lineCursor);

                        // Process each selected line
                        while ((lineFeature = (IFeature)lineCursor.NextRow()) != null)
                        {
                            if (lineFeature.Shape.GeometryType == esriGeometryType.esriGeometryPolyline)
                            {
                                selCurve = (IPolycurve3)lineFeature.ShapeCopy;
                                //polylinePointCollection = (IPointCollection)lineFeature.ShapeCopy;

                                // Get cursor of crossing features
                                spatialFilter.Geometry = selCurve;
                                spatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelCrosses;
                                featureCursor = lineLayer.Search(spatialFilter, false);

                                // Process each crossing feature
                                while ((crossFeature = (IFeature)featureCursor.NextFeature()) != null)
                                {
                                    topologicalOP = (ITopologicalOperator)crossFeature.Shape;
                                    //topologicalOP2 = (ITopologicalOperator)polylinePointCollection;
                                    topologicalOP2 = (ITopologicalOperator)selCurve;

                                    topologicalOP2.Simplify();
                                    topologicalOP.Simplify();
                                    intersectPointCollection = (IPointCollection)topologicalOP.Intersect((IGeometry)selCurve, esriGeometryDimension.esriGeometry0Dimension);

                                    for (int i = 0; i < intersectPointCollection.PointCount; i++)
                                    {
                                        intersectPoint = intersectPointCollection.get_Point(i);

                                        //Find distance along the original selected line
                                        //focusPolyline = (IPolyline)lineFeature.ShapeCopy;
                                        selCurveB = (IPolycurve3)lineFeature.ShapeCopy;
                                        selCurveB.QueryPointAndDistance(esriSegmentExtension.esriNoExtension, intersectPoint, false, outPoint, ref distAlongCurve, ref distFromCurve, ref boolRightSide);
                                        totalDistance = distAlongCurve + m_jumpDistance;

                                        //Continue if there is enough room
                                        if ((distAlongCurve >= m_jumpDistance) || (selCurveB.Length >= totalDistance))
                                        {
                                            //find the points where the curve will begin and end
                                            beginPoint = new PointClass();
                                            endPoint = new PointClass();
                                            selCurveB.QueryPoint(esriSegmentExtension.esriNoExtension, (distAlongCurve - m_jumpDistance), false, beginPoint);
                                            selCurveB.QueryPoint(esriSegmentExtension.esriNoExtension, (distAlongCurve + m_jumpDistance), false, endPoint);

                                            //split the original line at the two points (vertices for begin and end of new curve)
                                            projectOnto = true;
                                            createPart = false;
                                            selCurveB.SplitAtPoint(beginPoint, projectOnto, createPart, out splitHappenedBefore, out newPartIndexBegin, out newSegmentIndexBegin);
                                            selCurveB.SplitAtPoint(endPoint, projectOnto, createPart, out splitHappenedAfter, out newPartIndexEnd, out newSegmentIndexEnd);

                                            if ((splitHappenedBefore = true) && (splitHappenedAfter = true))
                                            {
                                                //Create the curve segment and add it to the polyline
                                                constructCircArc = new CircularArcClass();
                                                proximityOP = (IProximityOperator)intersectPoint;
                                                //pointDistance = proximityOP.ReturnDistance(beginPoint);
                                                pointDistance = m_jumpDistance;

                                                //check for direction of line to always make the jump on top
                                                if (_commandSubtype == 1)
                                                    if (endPoint.X > beginPoint.X)
                                                        constructCircArc.ConstructChordDistance(intersectPoint, beginPoint, false, (2 * pointDistance));
                                                    else
                                                        constructCircArc.ConstructChordDistance(intersectPoint, beginPoint, true, (2 * pointDistance));
                                                else
                                                    if (endPoint.X <= beginPoint.X)
                                                        constructCircArc.ConstructChordDistance(intersectPoint, beginPoint, false, (2 * pointDistance));
                                                    else
                                                        constructCircArc.ConstructChordDistance(intersectPoint, beginPoint, true, (2 * pointDistance));


                                                curveSegment = constructCircArc as ISegment;
                                                if (curveSegment != null & curveSegment.Length > 0)
                                                {
                                                    segmentCollNew = (ISegmentCollection)new PolylineClass();
                                                    segmentCollNew.AddSegment(curveSegment, ref _missing, ref _missing);
                                                    segmentColl = (ISegmentCollection)selCurveB;
                                                    segmentColl.ReplaceSegmentCollection(newSegmentIndexBegin, 1, segmentCollNew);

                                                    lineFeature.Shape = (IGeometry)selCurveB;
                                                    lineFeature.Store();

                                                    //Prepare to redraw area around feature
                                                    ext = curveSegment.Envelope;
                                                    ext.Expand(2, 2, true);
                                                   // invalid.Add(ext);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        Marshal.ReleaseComObject(lineCursor);
                    }
                }
                catch (Exception ex)
                {
                    editor.AbortOperation();
                    MessageBox.Show(ex.Message, ex.Source);
                }

                // Stop the edit operation 
                editor.StopOperation("Create Jumps");

                // Refresh
                //invalid.Invalidate((short)esriScreenCache.esriAllScreenCaches);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, ex.Source);
                return;
            }
        }

    }
}
